/**
* @author Meng Zhang
**/

#include "stdafx.h"
#include "Accelerator.h"
#include "../mfc/MainFrm.h"

using namespace std;

BOOL Accelerator::RegisterKey( UINT msg , int* modifier, int *key, int step /*= 1*/ ) {
	if (step>__MAX_ACCELERATOR_STEP)
		return FALSE;

	if (FindMsg(msg)!=kmap.end())
		return FALSE;
	KeyToCommand tmp;
	tmp.msg = msg;
	tmp.step = step;
	for (int i = 0;i<step;++i) {
		tmp.modifier[i] = modifier[i];
		tmp.key[i] = key[i];
	}

	//ether not exist in the kmap nor be a prefix of another one
	if (FindAccel(tmp))
		return FALSE;
	
	kmap.push_back(tmp);
	
	return TRUE;
}

int Accelerator::GetMaxStep() {
	return __MAX_ACCELERATOR_STEP;
}

Accelerator::Container::iterator Accelerator::FindMsg( UINT msg ) {
	for (Container::iterator iter = kmap.begin();iter!=kmap.end();++iter) {
		if (iter->msg==msg)
			return iter;
	}
	return kmap.end();
}

namespace {
	/*
		return value:
			1		:	same
			2		:	a is prefix of b
			0		:	other
	*/
	int Compare(const KeyToCommand& a , const KeyToCommand& b) {
		for (int i = 0;i<a.step;++i) {
			if (!(a.modifier[i]==b.modifier[i] && a.key[i]==b.key[i]))
				return 0;
		}
		return a.step==b.step?1:2;
	}
}

UINT Accelerator::FindAccel( const KeyToCommand& cmd ) {
	for (Container::iterator iter = kmap.begin();iter!=kmap.end();++iter) {
		if (cmd.step>iter->step)
			continue;
		int ret = Compare(cmd,*iter);
		if (ret==1)	//same
			return iter->msg;
		else if (ret == 2)	//be a prefix
			return -1;
	}
	return 0;
}
//#include "../mfc/resource.h"
void Accelerator::Initialize() {
	state.step = 0;
//  	int modifier[2] = {MOD_CONTROL | MOD_ALT,MOD_CONTROL};
//  	int key[2] = {(int)'C',(int)'F'};
//  	RegisterKey(ID_FILE_NEW_OPEN,&modifier[0],&key[0],2);
}

UINT Accelerator::GetAccel( int key, int modifier ) {
	if (state.step==__MAX_ACCELERATOR_STEP)
		state.step = 0;
	int now = state.step;
	state.modifier[now] = modifier;
	state.key[now] = key;
	++state.step;
	int ret = FindAccel(state);
	if (ret == 0)	//find nothing
		return state.step = 0;
	if (ret == -1)	// be a prefix
		return 0;
	//state.step = 0;
	return ret;
}

void Accelerator::Clear() {
	kmap.clear();
}

namespace {
	bool IsKeyDown(int key) {
		return (::GetKeyState(key) & 0x80000000) != 0;
	}
	bool IsValid(int key) {
		return key<0x87 && key >0x12;
	}
}

BOOL Accelerator::HandleMsg( MSG *pMsg ) {
	if (pMsg->message!=WM_KEYDOWN && pMsg->message!=WM_SYSKEYDOWN)
		return FALSE;
	
	int key = pMsg->wParam;

 	if (!IsValid(key))
 		return FALSE;

	int modifier = 0;
	if (IsKeyDown(VK_SHIFT))
		modifier |= MOD_SHIFT;
	if (IsKeyDown(VK_CONTROL))
		modifier |= MOD_CONTROL;
	if (IsKeyDown(VK_MENU))
		modifier |= MOD_ALT;

	int ret = GetAccel(key,modifier);
	UpdateStatusBar(this,(((CMainFrame*)AfxGetMainWnd())->GetStatusBar()));
	if (ret==0) return FALSE;
	state.step = 0;
	AfxGetMainWnd()->SendMessage(WM_COMMAND,MAKELONG(ret,1));
	return TRUE;
}
